#include "ControlCollege.hpp"
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>

ControlCollege::ControlCollege() {}

ControlCollege::~ControlCollege() {
  if (GCThread.joinable()) {
    GCThread.request_stop();
    GCqueue.Finish();
    GCThread.join();
  }
  for (auto &i : conlist)
    delete i.second;
}

ControlCollege &ControlCollege::operator<<(BaseDriver *driver) {
  for (auto [uuid, camera] : driver->GetCameraList())
    if (!conlist.insert({uuid, new ControlObj(this, driver, camera)}).second)
      throw std::runtime_error("Create Control Obj Failed");
  driverList.push_back(driver);
  return *this;
}

void ControlCollege::Boot() {
  GCThread = std::jthread([this](std::stop_token token) {
    std::string uuid;
    while (GCqueue.ConsumeSync(uuid) && !token.stop_requested()) {
      auto ptr = SearchListItem(uuid)->second;
      ptr->driver->CameraOffline(uuid);
      delete ptr;
      conlist.erase(SearchListItem(uuid));
      spdlog::info("Device {} is delete", uuid);
      funcMsg(fmt::format("Device {} is delete", uuid));
    }
  });
}

std::vector<std::string> ControlCollege::GetAllDevice() {
  for (auto &driver : driverList)
    for (auto &camera : driver->GetCameraList())
      if (conlist.find(camera.first) == conlist.end())
        if (conlist
                .insert(
                    {camera.first, new ControlObj(this, driver, camera.second)})
                .second)
          spdlog::info("Find a new camera {}", camera.first);

  std::vector<std::string> res;
  res.reserve(conlist.size());
  for (auto [uuid, camera] : conlist)
    res.push_back(uuid);
  return res;
}

void ControlCollege::StartShot(std::string uuid) {
  SearchListItem(uuid)->second->StartShot();
}

void ControlCollege::StopShot(std::string uuid) {
  SearchListItem(uuid)->second->StopShot();
}

bool ControlCollege::ShotStatus(std::string uuid) {
  return SearchListItem(uuid)->second->GetShotStatus();
}

void ControlCollege::StartRecord(std::string uuid, std::string path,
                                 int format) {
  SearchListItem(uuid)->second->StartRecord(formatList.at(format)->Build(),
                                            path);
}

void ControlCollege::StopRecord(std::string uuid) {
  SearchListItem(uuid)->second->StopRecord();
}

bool ControlCollege::RecordStatus(std::string uuid) {
  return SearchListItem(uuid)->second->GetRecordStatus();
}

std::vector<std::string> ControlCollege::GetAllFormat() {
  std::vector<std::string> res;
  res.reserve(formatList.size());
  for (auto &i : formatList)
    res.push_back(i->GetName());
  return res;
}

std::string /*json*/ ControlCollege::GetShareMemInfo(std::string uuid) {
  auto [sharename, semname, size] =
      SearchListItem(uuid)->second->GetShareMemInf();
  nlohmann::json j = nlohmann::json::object();
  j["MemName"] = sharename;
  j["SemName"] = semname;
  j["Size"] = size;
  return j.dump();
}

std::string /*json*/ ControlCollege::GetSettingList(std::string uuid) {
  return SearchListItem(uuid)->second->GetSettingList();
}

std::string /*json*/ ControlCollege::GetSettingValue(std::string uuid,
                                                     long setid) {
  return SearchListItem(uuid)->second->GetSettingValue(setid);
}

bool ControlCollege::SetSettingValue(std::string uuid,
                                     std::string value /*json*/) {
  return SearchListItem(uuid)->second->SetSettingValue(value);
}

decltype(ControlCollege::conlist)::iterator
ControlCollege::SearchListItem(const std::string &uuid) {
  auto ptr = conlist.find(uuid);
  if (ptr == conlist.end()) {
    spdlog::warn("ControlCollege StopShot Can't find uuid:{}", uuid);
    throw std::runtime_error("ControlCollege StopShot Can't find uuid");
  }
  return ptr;
}

void ControlCollege::HandleOffline(std::string uuid) {
  GCqueue.Produce(std::string(uuid));
  funcCameraOffline(uuid);
}

float ControlCollege::GetFPS(std::string uuid) {
  return SearchListItem(uuid)->second->GetFPS();
}

void ControlCollege::Offline(std::string uuid) { HandleOffline(uuid); }

std::string ControlCollege::GetPreviewImgInfo(std::string uuid) {
  nlohmann::json j;
  BaseCamera::ImgInf inf = SearchListItem(uuid)->second->GetPreviewImageInf();
  j["Height"] = inf.height;
  j["Width"] = inf.width;
  j["Format"] = [&]() -> long {
    switch (inf.type) {
    case BaseCamera::ImageType::GREY:
      return 1;
    case BaseCamera::ImageType::RGB8:
      return 2;
    };
  }();

  return j.dump();
}
